package javatuning.ch3.nio;

import android.os.FileObserver;
import android.support.annotation.Nullable;

import org.junit.Test;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.CharBuffer;
import java.nio.IntBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Timer;
import java.util.TimerTask;

import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;

/**
 * @author Administrator
 * 文件映射到内存，映射到内存,从哪里开始，映射多大
 */
public class TestMapBufferMe {
//	protected static int numOfInts = 4*1000*1000;
	protected static int numOfInts = 4;


	
	@Test
	public void testStreamWrite() throws IOException {
		long starttime = System.currentTimeMillis();
//		 DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(new File("temp.txt")),16*1024*1024));
		File file = new File("temp_stream.txt");
		DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
         for(int i = 0; i < numOfInts; i++){  
             dos.writeInt(i);
         }
         if(dos != null){
             dos.close();  
         }
		long endtime = System.currentTimeMillis();
         // testStreamWrite:290ms
		System.out.println("testStreamWrite:"+(endtime-starttime)+"ms");

//		renameFile2(file.getAbsolutePath(), "temp_stream_new.txt");

//		String fileName = "d:\\temp_buffer.txt";
//		String newdirPath = "d:\\testdir";
//		moveFile2NewDir2(file.getAbsolutePath(), newdirPath);
	}

	@Test
	public void testMappedWrite() throws Exception {
//		monitorFileForder();
		long starttime = System.currentTimeMillis();
		File file = new File("temp_stream.txt");
		RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
		FileChannel fc = randomAccessFile.getChannel();
		// 文件映射到内存:
		IntBuffer ib = fc.map(FileChannel.MapMode.READ_WRITE, 0, numOfInts*(4)).asIntBuffer();
		for (int i = 0; i < numOfInts; i++) {
			ib.put(i);
			System.out.println("position="+ib.position()+", limit:"+ib.limit());
		}
		randomAccessFile.close();
		if (fc != null) {
			fc.close();
			fc = null;
		}
		long endtime = System.currentTimeMillis();
		// testMappedWrite:133ms
		System.out.println("testMappedWrite:"+(endtime-starttime)+"ms");

//		Thread.sleep(3000);


		/*file = new File("temp_stream2.txt");
		randomAccessFile = new RandomAccessFile(file, "rw");
		fc = randomAccessFile.getChannel();
		// 文件映射到内存:
		ib = fc.map(FileChannel.MapMode.READ_WRITE, 0, numOfInts*(4)).asIntBuffer();
		for (int i = 0; i < numOfInts; i++) {
			ib.put(i);
		}
		randomAccessFile.close();
		if (fc != null) {
			fc.close();
			fc = null;
		}
*/

	}
	
	@Test
	public void testStreamRead() throws IOException {
		long starttime = System.currentTimeMillis();
		//DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream(new File("temp.txt")),16*1024*1024));
		File file = new File("temp_stream.txt");
		DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
        for(int i = 0; i < numOfInts; i++){  
            dis.readInt();  
        }  
        if(dis != null){  
            dis.close();  
        }  
		long endtime = System.currentTimeMillis();
        // testStreamRead:403ms
		System.out.println("testStreamRead:"+(endtime-starttime)+"ms");
	}
	 
	


	public void monitorFileForder() {
		File file = new File("D:\\testdir");
		FileObserver fileObserver = new FileObserver(file) {
			@Override
			public void onEvent(int event, @Nullable String path) {
				System.out.println(" path :"+path + ", event:"+event);
			}
		};
		fileObserver.startWatching();
	}
	
	@Test
	public void testMappedRead() throws IOException {
		long starttime = System.currentTimeMillis();
		FileChannel fc = new FileInputStream("temp_mapped.txt").getChannel();

        IntBuffer ib = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size())
				.asIntBuffer();
        while(ib.hasRemaining()){  
            ib.get();  
        }  
        if(fc != null){  
            fc.close();  
        }  
		long endtime = System.currentTimeMillis();
        // testMappedRead:33ms
		System.out.println("testMappedRead:"+(endtime-starttime)+"ms");
	}


	@Test
	public void testMappedWriteChar() throws IOException {
		int charCount = 10;
		long starttime = System.currentTimeMillis();
		RandomAccessFile randomAccessFile = new RandomAccessFile
				("temp_mapped_char.txt", "rw");
		FileChannel fc = randomAccessFile.getChannel();
		long size = fc.size();
		System.out.println("写文件前的长度 size="+size);
		String content = "abc";// 注意char 的size * 2, 应该有一个空格分开
		CharBuffer ib = fc.map(FileChannel.MapMode.READ_WRITE, size, content.length()*2)
				.asCharBuffer();
//		for (int i = 0; i < charCount; i++) {
//			System.out.println( i+" 次put b");
//			ib.put('b');
			ib.put(content);
//		}
		System.out.println("写文件后的长度 length="+randomAccessFile.length() + ", position="+ib.position());
		if (fc != null) {
			fc.close();
		}
		long endtime = System.currentTimeMillis();
		System.out.println("testMappedWriteChar:"+(endtime-starttime)+"ms");
	}

	@Test
	public void testMappedWriteChar2() throws IOException {
		RandomAccessFile randomAccessFile = new RandomAccessFile
				("temp_mapped_char.txt", "rw");
		FileChannel fc = randomAccessFile.getChannel();
		String content[] = {"aaa", "bb", "cccc"};
		CharBuffer ib = fc.map(FileChannel.MapMode.READ_WRITE, 0*2, content[0].length()*2)
				.asCharBuffer();
		System.out.println("fc.size() = " + fc.size());
		System.out.println("第一次put前position=" + ib.position() + ", limit="+ib.limit());
		ib.put(content[0]);
		System.out.println("fc.size() = " + fc.size());
		System.out.println("第一次put后position=" + ib.position() + ", limit="+ib.limit());

		int startPosition = ib.position()*2;
		ib = fc.map(FileChannel.MapMode.READ_WRITE, ib.position()*2, content[1].length()*2 + 10)
				.asCharBuffer();
		System.out.println("fc.size() = " + fc.size());
		System.out.println("第2次put前 position=" + ib.position() + ", limit="+ib.limit());
		ib.put(content[1]);
		System.out.println("fc.size() = " + fc.size());
		System.out.println("第2次put后 position=" + ib.position() + ", limit="+ib.limit());

		startPosition += ib.position()*2;
		System.out.println("第3次起始位置:" +startPosition);
		System.out.println("fc.size() = " + fc.size());
		ib = fc.map(FileChannel.MapMode.READ_WRITE, startPosition, content[2].length()*2)
				.asCharBuffer();
		System.out.println("fc.size() = " + fc.size());
		ib.put(content[2]);
		System.out.println("fc.size() = " + fc.size());
		if (fc != null) {
			fc.close();
		}
		// 获取文件真正大小(包含了多映射的大小,所以apk很麻烦)
		long length = new File("temp_mapped_char.txt").length();
		System.out.println("获取文件真正大小 length = " +length);
	}

	@Test
	public void testMappedWriteChar3() throws IOException {
		RandomAccessFile randomAccessFile = new RandomAccessFile
				("temp_mapped_char.txt", "rw");
		FileChannel fc = randomAccessFile.getChannel();

		String content[] = {"aaaaaa", "bbb"};
		int position = 0;
		for (int i = 0; i < content.length; i++) {
			// 一个字符占两个字节(CharBuffer应该是考虑中文)
			CharBuffer ib = fc.map(FileChannel.MapMode.READ_WRITE, position*2, content[i].length()*2)
					.asCharBuffer();
			System.out.println(i + " put前 position=" + ib.position() + ", limit="+ib.limit());
			ib.put(content[i]);
			position = ib.position();
			System.out.println(i+" put后position=" + ib.position() + ", limit="+ib.limit());
		}

		System.out.println("size="+fc.size()+", position="+fc.position());
		if (fc != null) {
			fc.close();
		}
	}

	@Test
	public void testMappedReadChar() throws IOException {
		long starttime = System.currentTimeMillis();
		FileChannel fc = new FileInputStream("temp_mapped_char.txt").getChannel();
		long size = fc.size();
		System.out.println("read size:"+size);
		CharBuffer ib = fc.map(FileChannel.MapMode.READ_ONLY, 0, size).asCharBuffer();
		while(ib.hasRemaining()){
			System.out.println("读取" + ib.get());
		}
		if(fc != null){
			fc.close();
		}
		long endtime = System.currentTimeMillis();
		// testMappedRead:33ms
		System.out.println("testMappedReadChar:"+(endtime-starttime)+"ms");
	}


	/**
	 * 1 向文件一分钟的数据，当然肯定是断断续续的写哈
	 * 2 肯定是未知大小
	 * 3 fc map 的时候需要指定大小，有多少数据来，就映射多少数据,从
	 *
	 * @throws IOException
	 */
	@Test
	public void testMappedWriteStr() throws Exception {
		String fileName = "temp_mapped_char.txt";
//		String fileName = "d:\\temp_buffer.txt";


		long starttime = System.currentTimeMillis();
		RandomAccessFile randomAccessFile = new RandomAccessFile
				(fileName, "rw");
		FileChannel fc = randomAccessFile.getChannel();
		int charCount = 10;
		int writeCount = 3;
		int currentWriteCount = 0;
		long size = 0;
		while (currentWriteCount < writeCount) {

			System.out.println(currentWriteCount + "次-> 写文件前的长度 size="+size);
			CharBuffer ib = fc.map(FileChannel.MapMode.READ_WRITE, size*2, charCount)
					.asCharBuffer();
			for (int i = 0; i < 3; i++) {
				System.out.println( i+" 次put b");
				ib.put('b');
			}
			size = ib.position();
			System.out.println("写文件后的长度 ib.position()="+size);
			System.out.println("fc.size() = "+fc.size() + ", position="+fc.position());
			currentWriteCount++;
		}
		System.out.println("");
		if (fc != null) {
			fc.close();
		}
		if (randomAccessFile != null) {
			randomAccessFile.close();
		}
		long endtime = System.currentTimeMillis();
		System.out.println("testMappedWriteChar:"+(endtime-starttime)+"ms");

		Thread.sleep(1000);


		/*Path source = FileSystems.getDefault().getPath(fileName);
		*//*Path target = source.resolveSibling("duanxia.txt");*//*
		System.out.println(source.getFileName());

		// java.nio.file.FileSystemException: d:\temp_buffer.txt -> d:\temp_buffer_copy.txt:
		// 另一个程序正在使用此文件，进程无法访问。
		Files.move(source, source.resolveSibling("temp_buffer_copy.txt"));*/

//		moveFile2NewDir();
	}

	@Test
	public void renameFile() throws IOException {
//		String fileName = "temp_mapped_char.txt";
		String fileName = "d:\\temp_buffer.txt";
		Path source = FileSystems.getDefault().getPath(fileName);
		// 注意目标文件不要加 目录哈
		Files.move(source, source.resolveSibling("temp_buffer_copy.txt"));
	}

	public boolean renameFile2(String sourceFileNameAbsolutePath, String simpleNewFileName) throws IOException {
		Path source = FileSystems.getDefault().getPath(sourceFileNameAbsolutePath);
		// 注意目标文件不要加 目录哈
		Files.move(source, source.resolveSibling(simpleNewFileName));
		System.out.println("rename success ");
		return true;
	}

	@Test
	public void moveFile2NewDir() throws IOException {
//		String fileName = "temp_mapped_char.txt";
		String fileName = "d:\\temp_buffer.txt";
		String newdirPath = "d:\\testdir";
		Path source = FileSystems.getDefault().getPath(fileName);
		// 注意目标文件不要加 目录哈
		Path newdir = FileSystems.getDefault().getPath(newdirPath);
		Files.move(source, newdir.resolve(source.getFileName()), REPLACE_EXISTING);
	}

	public void moveFile2NewDir2(String sourceFileNameAbsolutePath, String newDir) throws IOException {
//		String fileName = "d:\\temp_buffer.txt";
//		String newdirPath = "d:\\testdir";
		Path source = FileSystems.getDefault().getPath(sourceFileNameAbsolutePath);
		// 注意目标文件不要加 目录哈
		Path newdir = FileSystems.getDefault().getPath(newDir);
		Files.move(source, newdir.resolve(source.getFileName()), REPLACE_EXISTING);
	}

	@Test
	public void createChild() throws IOException {
		String parentPath = "d:\\testdir";
		String childPath = "temp";
		String filename = "text.txt";
		File parent = new File(parentPath, childPath);
		if (!parent.exists()) {
			parent.mkdir();
		}
		File file = new File(parent, filename);
		if (!file.exists()) {
			file.createNewFile();
		}
	}

	/**
	 * 文件读写的问题
	 * 1 并发问题: 必须锁住,否则抛异常
	 * 2 stream 不关产生的问题, 第一：fd 泄露,
	 * 第二: 读的时候抛异常： EOF异常,
	 * 第三: move的额时候：另外一段程序正在使用
	 *
	 * @throws Exception
	 */
	@Test
	public void testStreamReadWrite() throws Exception {
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					testStreamWrite2();
				}catch (Exception e) {
					e.printStackTrace();
				}

			}
		}).start();
		Thread.sleep(1);
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					testStreamRead2();
				}catch (Exception e) {
					e.printStackTrace();
				}
			}
		}).start();

		Thread.sleep(2000);
	}

	@Test
	public synchronized void testStreamWrite2() throws Exception {
		long starttime = System.currentTimeMillis();
		File file = new File("temp_stream.txt");
		DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
		for(int i = 0; i < 5; i++){
			System.out.println("写入 i = " + i);
			dos.writeUTF("1");
			Thread.sleep(1);
		}
		if(dos != null){
			dos.close();
		}

		/*long endtime = System.currentTimeMillis();
		System.out.println("testStreamWrite:"+(endtime-starttime)+"ms");
		String newdirPath = "d:\\testdir";
		moveFile2NewDir2(file.getAbsolutePath(), newdirPath);*/
	}

	public synchronized void testStreamRead2() throws Exception {
		long starttime = System.currentTimeMillis();
		File file = new File("temp_stream.txt");
		DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
		for(int i = 0; i < 5; i++){
			System.out.println(dis.readUTF());
			Thread.sleep(1);
		}
		if(dis != null){
			dis.close();
		}
		long endtime = System.currentTimeMillis();
		System.out.println("testStreamRead:"+(endtime-starttime)+"ms");
	}

	@Test
	public void testRealFileSize() throws Exception {
		File file = new File("temp_stream.txt");
		DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
		for(int i = 0; i < 5; i++){
			System.out.println("写入 i = " + i);
			dos.writeChar(i); // 你要明白文件系统(linux)不会立马落盘(情况非常少),所以你数据库记录的写入的size
			Thread.sleep(1);
		}
		if(dos != null){
			dos.close();
		}
		System.out.println("length = " + file.length());// bytes
	}

	@Test
	public void testWriteLog() throws Exception {
		WriteFileByMap writeFileByMap = new WriteFileByMap();
		writeFileByMap.start();
		for (int i = 0; i < 10; i++) {
			writeFileByMap.writen(new WriteFileByMap.Msg(WriteFileByMap.Msg.MSG_NORMAL, "abc"));
			System.out.println(String.format("第%s次sleep", i));
			Thread.sleep(300);
		}
//		read();
		Thread.sleep(1000);
	}

	public void read() throws IOException {
		long starttime = System.currentTimeMillis();
		FileChannel fc = new FileInputStream("temp_mapped_char.txt").getChannel();
		System.out.println("读取文件大小 " + fc.size());
		CharBuffer ib = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).asCharBuffer();
		while(ib.hasRemaining()){
			System.out.print(ib.get() + " ");
		}
		if(fc != null){
			fc.close();
		}
		long endtime = System.currentTimeMillis();
		// testMappedRead:33ms
		System.out.println("testMappedRead:"+(endtime-starttime)+"ms");
	}

	@Test
	public void testTimer() throws Exception {
		Timer timer = new Timer();
		timer.schedule(new MyTimerTask(),1000);
		Thread.sleep(3000);
	}
	class MyTimerTask extends TimerTask {
		@Override
		public void run() {
			System.out.println("run ...");
		}
	}
}
